package com.sky.aop;

import com.sky.annotation.AutoFill;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import org.apache.commons.lang3.ObjectUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author Administrator
 * @version 1.0
 * @description: 自动填充切面
 * @date 2024/1/18 9:55
 */
@Aspect
@Component
public class AutoFillAspect {
    /**
     * 1、定义切入点表达式（只关注mapper层且标注了@AutoFill的方法）
     */

    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
    public void doAspect(){}


    /**
     * 2、定义前置通知（进行属性的填充）
     */
    @Before("doAspect() && @annotation(autoFill)")
    public void doBefore(JoinPoint joinPoint,AutoFill autoFill) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        //1、获取数据库的操作类型
        /*MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        OperationType operationType = methodSignature.getMethod().getAnnotation(AutoFill.class).value();*/

        OperationType operationType = autoFill.value();

        //2、获取待填充的对象
        Object[] args = joinPoint.getArgs();
        if (ObjectUtils.isEmpty(args)) {
            return;
        }
        Object entity = args[0];

        //3、根据数据库的操作类型去填充对应的字段
        Class<?> entityClass = entity.getClass();
        Map<String, Method> methodMap = Arrays.stream(entityClass.getDeclaredMethods()).collect(Collectors.toMap(m -> m.getName(), Function.identity()));
        switch (operationType){
            case INSERT:
                //插入时，自动填充创建时间、创建人、更新时间、更新人
                /*fieldFill(entityClass, entity,"createTime");
                fieldFill(entityClass, entity,"createUser");
                fieldFill(entityClass, entity,"updateTime");
                fieldFill(entityClass, entity,"updateUser");*/

                methodFill(methodMap, entity,"setUpdateUser");
                methodFill(methodMap, entity,"setUpdateTime");
                methodFill(methodMap, entity,"setCreateUser");
                methodFill(methodMap, entity,"setCreateTime");
                break;
            case UPDATE:
                //更新时，自动填充更新时间、更新人
                methodFill(methodMap, entity,"setUpdateUser");
                methodFill(methodMap, entity,"setUpdateTime");
                break;
            default:
                break;
        }
    }

    /**
     * 字段填充（方法反射）
     * @param methodMap
     * @param entity
     * @param methodName
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    private void methodFill(Map<String, Method> methodMap, Object entity, String methodName) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Method method = methodMap.get(methodName);
        if(method == null){
            return;
        }
        if (methodName.contains("Time")) {
            method.invoke(entity, LocalDateTime.now());
        }else{
            method.invoke(entity, BaseContext.getCurrentId());
        }
    }

    /**
     * 字段填充（字段反射）
     * @param entityClass
     * @param entity
     * @param fieldName
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    private static void fieldFill(Class<?> entityClass, Object entity,String fieldName) throws NoSuchFieldException, IllegalAccessException {
        Field field = entityClass.getDeclaredField(fieldName);
        if (ObjectUtils.isEmpty(field)) {
            return;
        }
        field.setAccessible(true);
        if (field.getType() == LocalDateTime.class) {
            field.set(entity, LocalDateTime.now());
        } else {
            field.set(entity, BaseContext.getCurrentId());
        }
    }

}
